package com.system.boot.rbac.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.system.api.rbac.entity.po.SysMenu;
import com.system.api.rbac.entity.vo.MenuTreeVo;
import com.system.api.setting.entity.po.SysIcon;
import com.system.api.setting.enums.MenuTypeEnum;
import com.system.boot.rbac.mapper.MenuMapper;
import com.system.boot.rbac.mapper.RoleMenuRelationMapper;
import com.system.boot.rbac.service.MenuService;
import com.system.common.enums.FactoryEnum;
import com.system.common.factory.CacheFactory;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.yang.common.core.constant.SystemConstant;
import org.yang.common.core.exception.BaseException;
import org.yang.common.i18n.constant.I18nMessageConstant;
import org.yang.common.utils.jwt.JwtTokenAnalysisUtli;
import org.yang.common.utils.string.StringUtils;
import org.yang.database.orm.entity.BaseModel;
import org.yang.database.orm.entity.BaseQuery;

import java.util.*;
import java.util.stream.Collectors;

import static com.system.common.constant.MessageConstant.*;

/**
 * @author: lslands
 * @description:
 * @version：v1.0
 * @date: 2024/6/1 23:30
 */
@Slf4j
@Service
@RequiredArgsConstructor
public class MenuServiceImpl extends ServiceImpl<MenuMapper, SysMenu> implements MenuService {

    private final CacheFactory cacheFactory;
    private final RoleMenuRelationMapper roleMenuRelationMapper;

    @Override
    public void isExists(String title) throws BaseException {
        boolean existsByCode = baseMapper.getExistsByParams(title);
        if(existsByCode){
            throw new BaseException(BaseException.REPEAT,title);
        }
    }

    @Override
    public int handInsert(SysMenu record) throws BaseException {
        this.isExists(record.getTitle());
        if(MenuTypeEnum.LICK.getValue().equals(record.getType())){
            if(!StringUtils.ishttp(record.getPath())){
                throw new BaseException(LINK_MUST_HTTP_START);
            }
        }
        int result = baseMapper.insert(record);
        this.resetCache(result > 0);
        return result;
    }

    @Override
    public int handUpdateById(SysMenu record) throws BaseException {
        SysMenu oldResult = Optional.ofNullable(baseMapper.selectById(record.getId()))
                .orElseThrow(() -> new BaseException(BaseException.NULL));
        if (!Objects.equals(record, oldResult)) {
            if (!record.getTitle().equals(oldResult.getTitle())) {
                this.isExists(record.getTitle());
            }
            if(MenuTypeEnum.LICK.getValue().equals(record.getType())){
                if(!StringUtils.ishttp(record.getPath())){
                    throw new BaseException(LINK_MUST_HTTP_START);
                }
            }
            int result = baseMapper.updateById(record);
            this.resetCache(result > 0);
            return result;
        }
        return 1;
    }

    @Override
    public int handEnabledById(Long id, Integer enabled, Integer revision) throws BaseException {
        SysMenu oldResult = Optional.ofNullable(baseMapper.selectById(id))
                .orElseThrow(()-> new BaseException(BaseException.NULL));
        if(null==enabled){
            throw new BaseException(I18nMessageConstant.VALIDATION_STATUS_EMPTY);
        }
        if(enabled.equals(oldResult.getEnabled())){
            return 1;
        }
        int result = baseMapper.updateById(new SysMenu(id, enabled, revision));
        this.resetCache(result > 0);
        return result;
    }



    @Override
    public List<MenuTreeVo> getList(SysMenu record, List<String> roles) throws BaseException {
        List<SysMenu> menuList;
        if(JwtTokenAnalysisUtli.isRoot()){
            List<? extends Number> companyId = List.of(record.getCompanyId(), 0);
            LambdaQueryWrapper<SysMenu> wrapper = new LambdaQueryWrapper<SysMenu>()
                    .select(BaseModel::getId,SysMenu::getParentId,SysMenu::getTitle,SysMenu::getIcon,SysMenu::getRedirect,SysMenu::getComponent,BaseModel::getEnabled,SysMenu::getVisible)
                    .like(StringUtils.isNotBlank(record.getTitle()), SysMenu::getTitle, record.getTitle())
                    .eq(record.getEnabled()!=null,SysMenu::getEnabled,record.getEnabled())
                    .in(SysMenu::getCompanyId,companyId)
                    .orderByAsc(BaseModel::getId);
            menuList= baseMapper.selectList(wrapper);
        }else{
            menuList=roleMenuRelationMapper.selectMenuListByRoles(record,roles);
        }
        if(!menuList.isEmpty()){
            Set<Long> menuIds = menuList.stream().map(SysMenu::getId).collect(Collectors.toSet());
            return menuList.stream().map(menu -> {
                Long parentId = menu.getParentId();
                if (!menuIds.contains(parentId)) {
                    menuIds.add(parentId);
                    return recursion(parentId, menuList);
                }
                return new LinkedList<MenuTreeVo>();
            }).collect(ArrayList::new, ArrayList::addAll, ArrayList::addAll);
        }
        return new ArrayList<>();
    }
    /**
     * 递归菜单列表
     * @param parentId 父级ID
     * @param menuList 菜单列表
     * @return List<MenuTreeVo>
     */
    private static List<MenuTreeVo> recursion(Long parentId, List<SysMenu> menuList) {
        return Optional.ofNullable(menuList).orElse(new ArrayList<>())
                .stream().filter(menu -> menu.getParentId().equals(parentId))
                .map(menu -> {
                    MenuTreeVo routes = new MenuTreeVo();
                    BeanUtil.copyProperties(menu, routes);
                    List<MenuTreeVo> children = recursion(menu.getId(), menuList);
                    routes.setChildren(children);
                    return routes;
                }).collect(Collectors.toList());
    }

    @Override
    public int deleteOne(Long id) throws BaseException {
        if( Objects.isNull(id)){
            throw new BaseException("id cannot be null");
        }
        int result = baseMapper.deleteById(id);
        this.resetCache(result>0);
        return result;
    }

    @Override
    public int deleteBatch(Boolean delete,List<Long> ids) throws BaseException {
        if(ids.isEmpty()){
            return 0;
        }
        int result=delete?baseMapper.deleteBatch(ids):baseMapper.deleteBatchIds(ids);
        this.resetCache(result>0);
        return result;
    }

    @Override
    public void resetCache(boolean flag) {
        if (flag) {
            try {
                // 工厂方法
                cacheFactory.implementCacheProcessor(FactoryEnum.MENU.getType()).resetCache();
            } catch (BaseException e) {
                log.error("刷新菜单缓存异常:{}", e.getMessage());
            }
        }
    }
}
